package userservice

import (
	"context"
	"crypto/md5"
	"errors"
	"fmt"
	"gitee.com/go-mid/auth/authservice"
	"gitee.com/go-mid/idgen/idgenservice"
	"gitee.com/go-mid/infra/xlog"
	"gitee.com/go-mid/infra/xsql/xdb"
	"gitee.com/go-mid/user/internal/component"
	"gitee.com/go-mid/user/internal/dao"
	"gitee.com/go-mid/user/internal/entity"
	"gitee.com/go-mid/user/internal/util"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"strconv"
	"strings"
	"time"
)

//生成userId
func (m *UserServiceImpl) newOneUserId(ctx context.Context) (r int64, err error) {
	fun := "UserServiceImpl.newOneUserId -->"
	res, err := idgenservice.DefaultIdgenService.GetId(ctx, &idgenservice.GetIdReq{
		Sname:     "user",
		Namespace: "default",
	})
	if err != nil {
		xlog.Warnf(ctx, "%s new userId error: res: %v", fun, err)
		return 0, err
	}
	return res.Id, nil
}

type EntityUser struct {
	entity.User
	Attrs map[string]*entity.UserAttr `json:"attrs"`
}

// 获取用户信息
// 数据库中找不到，则返回一个空的对象，使用时需要判断空指针
func (m *UserServiceImpl) userEntity(ctx context.Context, userID, actor int64, fillDefault bool) (*EntityUser, error) {
	fun := "UserServiceImpl.userEntity -->"
	if userID <= 0 {
		xlog.Infof(ctx, "%s invalid userID: %d ", fun, userID)
		return nil, fmt.Errorf("invalid userid: %d", userID)
	}
	info, err := m.getUserCache(ctx, userID)
	if err != nil {
		xlog.Warnf(ctx, "%s get error userID: %d  err: %s", fun, userID, err)
		return nil, err
	}
	//说明缓存没有找到，从db中拉取
	//此处没有考虑缓存击穿问题 todo
	if info == nil || info.UserID == 0 {
		info = &EntityUser{
			Attrs: make(map[string]*entity.UserAttr),
		}
		itemUser, err := dao.GetUser(ctx, component.XDBUser, userID)
		if err != nil {
			xlog.Warnf(ctx, "%s GetUser error userID: %d  err: %s", fun, userID, err)
			return nil, err
		}
		if itemUser != nil {
			info.User = *itemUser
		}
		attrs, err := dao.ListUserAttrs(ctx, component.XDBUser, userID)
		if err != nil {
			xlog.Warnf(ctx, "%s GetUserattr error userID: %d  err: %s", fun, userID, err)
			return nil, err
		}
		for _, attr := range attrs {
			info.Attrs[attr.Attr] = attr
		}
		m.setUserCache(ctx, userID, info)
	}

	if fillDefault && info != nil {
		//将默认的空白属性填充
		m.fillDefaultnfo(ctx, info)
	}
	return info, nil
}

//todo 空实现
func (m *UserServiceImpl) getUserCache(ctx context.Context, userID int64) (*EntityUser, error) {
	fun := "getUserCache -->"
	if userID <= 0 {
		xlog.Infof(ctx, "%s invalid userID: %d ", fun, userID)
		return nil, fmt.Errorf("invalid userID: %d", userID)
	}
	userCacheKey := itemUserCacheKey(userID)
	xlog.Infof(ctx, "%s cache key: %s", fun, userCacheKey)
	return nil, nil
}

//todo 空实现
func (m *UserServiceImpl) setUserCache(ctx context.Context, userID int64, entityUser *EntityUser) error {
	fun := "getUserCache -->"
	if userID <= 0 {
		xlog.Infof(ctx, "%s invalid userID: %d ", fun, userID)
		return fmt.Errorf("invalid userID: %d", userID)
	}
	userCacheKey := itemUserCacheKey(userID)
	xlog.Infof(ctx, "%s cache key: %s", fun, userCacheKey)

	return nil
}
func (m *UserServiceImpl) delUserCache(ctx context.Context, userID int64) error {
	fun := "delUserCache -->"
	if userID <= 0 {
		xlog.Infof(ctx, "%s invalid userID: %d ", fun, userID)
		return nil
	}
	userCacheKey := itemUserCacheKey(userID)
	xlog.Infof(ctx, "%s cache key: %s", fun, userCacheKey)
	return nil
}
func (m *UserServiceImpl) fillDefaultnfo(ctx context.Context, user *EntityUser) {
	if user == nil {
		return
	}
	//if user.Name == "" {
	//	user.Name = getDefaultNmae(ctx)
	//}
	//if user.AvatarOrigin == "" {
	//	user.AvatarOrigin = getDefaultAvatarOrigin(ctx)
	//}
	//if user.AvatarTiny == "" {
	//	user.AvatarTiny = getDefaultAvatarTiny(ctx)
	//}
}

//针对个人获取token时进行加锁
func (m *UserServiceImpl) getToken(ctx context.Context, req *authservice.GetTokenReq) (authInfo *AuthInfo, err error) {
	fun := "auth -->"
	lockKey := fmt.Sprintf("token.%d", req.UserId)
	err = util.DistributeLockExec(ctx, lockKey, time.Second*15, time.Second*5, func() error {
		user, er := m.userEntity(ctx, req.UserId, 0, true)
		if er != nil {
			xlog.Warnf(ctx, "%s get user error: req: %v, err: %v ", fun, req, er)
			return er
		}
		authRes, err := authservice.DefaultAuthService.GetToken(ctx, req)
		if err != nil {
			xlog.Warnf(ctx, "%s get token error: req: %v, err: %v ", fun, req, err)
			return err
		}
		authInfo = &AuthInfo{
			UserInfo:           m.toUserInfo(ctx, user),
			AccessToken:        authRes.AccessToken,
			AccessTokenExpire:  authRes.AccessTokenExpire,
			RefreshToken:       authRes.RefreshToken,
			RefreshTokenExpire: authRes.RefreshTokenExpire,
		}
		return nil
	})
	return
}
func hashValue(val string) string {
	if len(val) == 0 {
		return ""
	}
	px := strings.ToLower(fmt.Sprintf("%x", md5.Sum([]byte(val))))
	return px
}

//执行注册
func (m *UserServiceImpl) doRegister(ctx context.Context, req *RegisterReq) (int64, error) {
	oldUserId := req.UserId
	var userId int64
	if oldUserId > 0 {
		var lockKey = fmt.Sprintf("registerOld.%d", oldUserId)
		//有userid的情况，需要分布式锁 处理并发
		err := util.DistributeLockExec(ctx, lockKey, time.Second*15, time.Second*5, func() error {
			uid, err := m.doRegisterByOldUserId(ctx, &req.RegisterParam)
			userId = uid
			return err
		})
		return userId, err
	}
	return m.doRegisterNewUser(ctx, &req.RegisterParam)
}
func (m *UserServiceImpl) doRegisterNewUser(ctx context.Context, req *RegisterParam) (int64, error) {
	fun := "doRegisterNewUser -->"
	var userId int64
	var err error
	userId, err = m.newOneUserId(ctx)
	if err != nil {
		xlog.Warnf(ctx, "%s get new userId error req: %v err: %s", fun, req, err)
		return 0, errors.New("get userId error")
	}
	if userId <= 0 {
		xlog.Warnf(ctx, "%s get invalid new userId error req: %v userId: %d ", fun, req, userId)
		return 0, errors.New("get userId fail")
	}
	//构建账号信息
	now := time.Now().Unix()
	var registerLoginId = &entity.UserLoginid{
		Ct:          now,
		Ut:          now,
		State:       int32(EnumLoginidState_NORMAL),
		Appid:       req.Appid,
		UserID:      userId,
		LoginidType: int32(req.LoginidType),
		Loginid:     m.encryLoginid(ctx, req.LoginID),
		UserPoolID:  req.UserPoolID,
	}
	loginidUps, err := xdb.BuildDbSqlMap(ctx, registerLoginId, true)
	if err != nil {
		xlog.Warnf(ctx, "%s build loginid ups error: req: %v, err: %v", fun, req, err)
		return 0, err
	}
	var registerUser = &entity.User{
		UserID:         userId,
		Ct:             now,
		Ut:             now,
		State:          int32(EnumUserState_NORMAL),
		RegisterStatus: int32(req.RegisterStatus),
	}

	if req.LoginCode.LoginCode != "" {
		switch req.LoginCodeType {
		case EnumLoginCodeType_Passwd:
			registerUser.Passwd = hashValue(req.LoginCode.LoginCode)
		}
	}
	userUps, err := xdb.BuildDbSqlMap(ctx, registerUser, true)
	if err != nil {
		xlog.Warnf(ctx, "%s build registerUser ups error: req: %v, err: %v", fun, req, err)
		return 0, err
	}
	//事务 保存账号和登录凭证
	err = component.XDBUser.TxExecute(ctx, func(db xdb.XDB) error {
		_, err := db.Insert(ctx, entity.UserLoginidTable, []map[string]interface{}{loginidUps})
		if err != nil {
			return err
		}
		_, err = db.Insert(ctx, entity.UserTable, []map[string]interface{}{userUps})
		if err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		xlog.Warnf(ctx, "%s register user err req: %v err: %s", fun, req, err)
		return 0, err
	}
	return userId, nil
}

//原有userId是游客的情况，针对该游客userId加锁执行注册，
//注意该函数并没有处理并发，
//在上层调用时处理并发
func (m *UserServiceImpl) doRegisterByOldUserId(ctx context.Context, req *RegisterParam) (int64, error) {
	fun := "doRegisterByOldUserId -->"
	oldUserId := req.UserId
	var userId int64
	var err error

	updateGuest := false
	oldUser, er := m.userEntity(ctx, oldUserId, 0, false)
	if er != nil {
		xlog.Warnf(ctx, "%s get old user error req: %v err: %s", fun, req, er)
	}
	//传入的userId是游客
	if oldUser != nil && oldUser.UserID > 0 && oldUser.RegisterStatus == int32(EnumRegisterStatus_Guest) {
		userId = oldUserId
		updateGuest = true
	}
	//使用游客的userId
	if updateGuest {
		userId, err = m.doRegisterFromGuest(ctx, req)
	} else {
		userId, err = m.doRegisterNewUser(ctx, req)
	}
	return userId, err
}
func (m *UserServiceImpl) doRegisterFromGuest(ctx context.Context, req *RegisterParam) (int64, error) {
	fun := "doRegisterFromGuest -->"
	var userId = req.UserId
	if userId <= 0 {
		return 0, errors.New("no guest userId")
	}
	var err error
	//构建账号信息
	now := time.Now().Unix()
	var registerLoginId = &entity.UserLoginid{
		Ct:          now,
		Ut:          now,
		State:       int32(EnumLoginidState_NORMAL),
		Appid:       req.Appid,
		UserID:      userId,
		LoginidType: int32(req.LoginidType),
		Loginid:     m.encryLoginid(ctx, req.LoginID),
		UserPoolID:  req.UserPoolID,
	}
	loginidUps, err := xdb.BuildDbSqlMap(ctx, registerLoginId, true)
	if err != nil {
		xlog.Warnf(ctx, "%s build loginid ups error: req: %v, err: %v", fun, req, err)
		return 0, err
	}
	var updateUser = &entity.User{
		Ct:             now,
		Ut:             now,
		State:          int32(EnumUserState_NORMAL),
		RegisterStatus: int32(EnumRegisterStatus_Register),
	}
	userUps, err := xdb.BuildDbSqlMap(ctx, updateUser, true)
	if err != nil {
		xlog.Warnf(ctx, "%s build updateUser ups error: req: %v, err: %v", fun, req, err)
		return 0, err
	}

	//事务 保存账号和登录凭证
	err = component.XDBUser.TxExecute(ctx, func(db xdb.XDB) error {
		_, err := db.Insert(ctx, entity.UserLoginidTable, []map[string]interface{}{loginidUps})
		if err != nil {
			return err
		}
		_, err = db.Update(ctx, entity.UserTable, map[string]interface{}{
			"user_id": userId,
		}, userUps)
		if err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		xlog.Warnf(ctx, "%s register user err req: %v err: %s", fun, req, err)
		return 0, err
	}
	//删除之前的游客缓存信息
	m.delUserCache(ctx, userId)
	return userId, nil
}
func (m *UserServiceImpl) postProcessRegister(ctx context.Context, userId int64, req *RegisterReq) {
	fun := "postProcessRegister -->"
	event := EventRegister{
		NewUserId:     userId,
		RegisterParam: req.RegisterParam,
		Timestamp:     time.Now().Unix(),
	}
	xlog.StatInfow(ctx, "register", "data", event)
	go func() {
		//	todo
		xlog.Infof(ctx, "%s write register mq: event: %v", fun, event)
	}()
}
func (m *UserServiceImpl) postProcessLogin(ctx context.Context, authInfo *AuthInfo, req *LoginReq) {
	fun := "postProcessLogin -->"
	event := EventLogin{
		LoginParam: req.LoginParam,
		Timestamp:  time.Now().Unix(),
	}
	xlog.StatInfow(ctx, "login", "authInfo", authInfo, "data", event)
	go func() {
		//	todo
		xlog.Infof(ctx, "%s write login mq: event: %v", fun, event)
	}()
}
func (m *UserServiceImpl) postProcessUpdate(ctx context.Context, userID int64, oldValue string, attr *UserAttr) {
	fun := "postProcessUpdate -->"
	event := EventUserUpdate{
		UserId:    userID,
		Attr:      attr.Attr,
		OldValue:  oldValue,
		NewValue:  attr.Val,
		Timestamp: time.Now().Unix(),
	}
	xlog.StatInfow(ctx, "updateAttr", "data", event)

	go func() {
		//todo mq事件 通知账号变更
		xlog.Infof(ctx, "%s write update mq: %v", fun, event)
	}()
}

func itemUserCacheKey(userId int64) string {
	return strconv.FormatInt(userId, 10)
}

func (m *UserServiceImpl) login(ctx context.Context, req *LoginParam) (*AuthInfo, error) {
	fun := "UserServiceImpl.login -->"
	//执行登录
	authInfo, err := m.getToken(ctx, &authservice.GetTokenReq{
		UserId:     req.UserId,
		AuthType:   req.AuthType,
		ClientInfo: req.ClientInfo,
		Appid:      int(req.Appid),
	})
	if err != nil {
		xlog.Warnf(ctx, "%s login error  req: %v err: %s", fun, req, err)
		return nil, status.Error(codes.Internal, err.Error())
	}
	//登录后置处理器
	m.postProcessLogin(ctx, authInfo, &LoginReq{
		LoginParam: LoginParam{
			LoginID: LoginID{
				LoginidType: req.LoginidType,
				Loginid:     req.Loginid,
				UserPoolID:  req.UserPoolID,
				Appid:       req.Appid,
			},
			UserId:     req.UserId,
			ClientInfo: req.ClientInfo,
			AuthType:   req.AuthType,
		},
	})
	return authInfo, nil
}
